﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Transactions;
using FimetContoDeposito.BLL.Objects;
using FimetContoDeposito.DAL;
using GrayParrot.Core;

namespace FimetContoDeposito.BLL.Facades
{
    public enum TipoAssociazioneCodiceFornitore
    {
        New=0,
        Update
    }

    public class CodiciFornitoriFacade : FacadeBase<CodiciFornitoriFacade>
    {
        FCDContext m_ctx = null;

        public Repository<FCD_CodiciFornitori, Guid> Repo { get; internal set; }


        public CodiciFornitoriFacade()
        {
        }

        public override void Initialize()
        {
            m_ctx = new FCDContext();
            m_ctx.Configuration.LazyLoadingEnabled = false;
            m_ctx.Configuration.ProxyCreationEnabled = false;

            Repo = this.GetRepository<FCD_CodiciFornitori, Guid>(m_ctx);
        }

        public override void Dispose()
        {
            if (m_ctx != null)
            {
                m_ctx.Dispose();

                Repo.Dispose();
            }
        }

        public ReturnValue<bool> Add(FCD_CodiciFornitori item)
        {
            ReturnValue<bool> retValue = new ReturnValue<bool>();

            try
            {
                if (item.Id == Guid.Empty)
                {
                    item.Id = Guid.NewGuid();
                }

                this.Repo.Add(item);
                this.Repo.Save();

                retValue.HasValue = true;
                retValue.Value = true;
            }
            catch (Exception ex)
            {
                retValue.HasError = true;
                retValue.Error = ex;
            }

            return retValue;
        }

        public ReturnValue<bool> Edit(FCD_CodiciFornitori item)
        {
            ReturnValue<bool> retValue = new ReturnValue<bool>();

            try
            {
                this.Repo.Edit(item);
                this.Repo.Save();

                retValue.HasValue = true;
                retValue.Value = true;
            }
            catch (Exception ex)
            {
                retValue.HasError = true;
                retValue.Error = ex;
            }

            return retValue;
        }

        public ReturnValue<bool> Delete(FCD_CodiciFornitori item)
        {
            ReturnValue<bool> retValue = new ReturnValue<bool>();

            try
            {
                this.Repo.Delete(item);
                this.Repo.Save();

                retValue.HasValue = true;
                retValue.Value = true;
            }
            catch (Exception ex)
            {
                retValue.HasError = true;
                retValue.Error = ex;
            }

            return retValue;
        }

        public ReturnValue<bool> Delete(Guid idArticolo)
        {
            ReturnValue<bool> retValue = new ReturnValue<bool>();

            try
            {
                ReturnValue<FCD_CodiciFornitori> obj = this.Get(idArticolo);
                if (obj.HasValue)
                {
                    retValue = this.Delete(obj.Value);
                }
            }
            catch (Exception ex)
            {
                retValue.HasError = true;
                retValue.Error = ex;
            }

            return retValue;
        }

        public ReturnValue<FCD_CodiciFornitori> Get(Guid id)
        {
            ReturnValue<FCD_CodiciFornitori> retValue = new ReturnValue<FCD_CodiciFornitori>();

            try
            {
                retValue.Value = this.Repo.Get(id);
                retValue.HasValue = true;
            }
            catch (Exception ex)
            {
                retValue.HasError = true;
                retValue.Error = ex;
            }

            return retValue;
        }

        public ReturnValue<FCD_CodiciFornitori> Get(Guid id, string includes)
        {
            ReturnValue<FCD_CodiciFornitori> retValue = new ReturnValue<FCD_CodiciFornitori>();

            try
            {
                retValue.Value = this.Repo.Get(x => x.Id == id, null, includes).FirstOrDefault();
                retValue.HasValue = true;
            }
            catch (Exception ex)
            {
                retValue.HasError = true;
                retValue.Error = ex;
            }

            return retValue;
        }

        public ReturnValue<IQueryable<FCD_CodiciFornitori>> GetAll()
        {
            ReturnValue<IQueryable<FCD_CodiciFornitori>> retValue = new ReturnValue<IQueryable<FCD_CodiciFornitori>>();
            retValue.HasValue = false;
            retValue.Value = null;

            try
            {
                retValue.Value = this.Repo.GetAll();
                retValue.HasValue = true;
            }
            catch (Exception ex)
            {
                retValue.HasError = true;
                retValue.Error = ex;
            }

            return retValue;
        }

        public ReturnValue<IQueryable<FCD_CodiciFornitori>> GetAllByFornitore(Guid m_id)
        {
            ReturnValue<IQueryable<FCD_CodiciFornitori>> retValue = new ReturnValue<IQueryable<FCD_CodiciFornitori>>(null);
            retValue.HasValue = false;

            try
            {
                var objs = from x in m_ctx.FCD_CodiciFornitoris.Include("FCD_Customer")
                           orderby x.Codice
                           where x.IdFornitore == m_id
                           select x;
                
                if (objs.Any())
                {
                    retValue.Value = objs;
                    retValue.HasValue = true;
                }
            }
            catch (Exception ex)
            {
                retValue.HasError = true;
                retValue.Error = ex;
            }

            return retValue;
        }

        public ReturnValue<Guid> GetFornitore(string codice)
        {
            ReturnValue<Guid> retValue = new ReturnValue<Guid>(Guid.Empty);
            retValue.HasValue = false;

            try
            {
                var objs = from x in m_ctx.FCD_CodiciFornitoris
                           where x.Codice == codice
                           select x;

                if (objs.Any())
                {
                    retValue.Value = objs.FirstOrDefault().IdFornitore;
                    retValue.HasValue = true;
                }
            }
            catch (Exception ex)
            {
                retValue.HasError = true;
                retValue.Error = ex;
            }

            return retValue;
        }

        public ReturnValue<Guid> GetFornitore(Guid idCodice)
        {
            ReturnValue<Guid> retValue = new ReturnValue<Guid>(Guid.Empty);
            retValue.HasValue = false;

            try
            {
                var objs = from x in m_ctx.FCD_CodiciFornitoris
                           where x.Id == idCodice
                           select x;

                if (objs.Any())
                {
                    retValue.Value = objs.FirstOrDefault().IdFornitore;
                    retValue.HasValue = true;
                }
            }
            catch (Exception ex)
            {
                retValue.HasError = true;
                retValue.Error = ex;
            }

            return retValue;
        }

        public ReturnValue<IQueryable<ArticoloBag>> GetAllByCodiceFornitore(Guid m_idCodice)
        {
            ReturnValue<IQueryable<ArticoloBag>> retValue = new ReturnValue<IQueryable<ArticoloBag>>(null);
            retValue.HasValue = false;

            
            //using (AnagraficaArticoliFacade m_dc = AnagraficaArticoliFacade.Create())
            //{
            //    ReturnValue<ArticoloBag> m_objs = m_dc.GetArticoloBag(id);
            //    if (m_objs.HasValue)
            //    {
            //        obj = m_objs.Value;
            //    }
            //}


            return retValue;
        }

        /// <summary>
        /// Ritorna il codice fornitore per un determinato articolo...
        /// </summary>
        /// <param name="idArticolo">The identifier articolo.</param>
        /// <param name="idFornitore">The identifier fornitore.</param>
        /// <param name="idCustomer">The identifier customer.</param>
        /// <returns></returns>
        /// <remarks>
        /// Il codice fornitore viene assegnato ad un articolo di un cliente.
        /// Codici di fornitori differenti potrebbero essere assegnati allo stesso articolo di un cliente.
        /// Il metodo ritorna, fissato il cliente, l'articolo ed il fornitore, l'eventuale codice fornitore assegnato. 
        /// </remarks>
        public ReturnValue<List<NameValue<string, Guid>>> GetCodiceFornitoreArticolo(Guid idArticolo, Guid idFornitore, Guid idCustomer)
        {
            ReturnValue<List<NameValue<string, Guid>>> retValue = new ReturnValue<List<NameValue<string, Guid>>>(null);
            retValue.HasValue = false;

            var objs = from x in m_ctx.FCD_GetCodiceFornitoreArticolo(idArticolo, idCustomer, idFornitore)
                       select new NameValue<string, Guid>()
                       {
                           name = x.codice,
                           value = x.id
                       };

            if (objs.Any())
            {
                retValue.HasValue = true;
                retValue.Value = objs.ToList();
            }

            return retValue;
        }

        public ReturnValue<List<NameValue<string, Guid>>> GetCodiciFornitoriArticoli(Guid idFornitore, Guid idCustomer)
        {
            ReturnValue<List<NameValue<string, Guid>>> retValue = new ReturnValue<List<NameValue<string,Guid>>>(null);
            retValue.HasValue = false;

            var objs = from x in m_ctx.FCD_GetCodiciFornitoriClienti(idFornitore, idCustomer)
                       select new NameValue<string, Guid>() { 
                        name = x.codice,
                        value = x.id
                       };

            if (objs.Any())
            {
                retValue.HasValue = true;
                retValue.Value = objs.ToList();
            }

            return retValue;
        }

        /// <summary>
        /// Ritorna tutti i codici fornitore per uno specifico cliente...
        /// </summary>
        /// <param name="idCustomer">The identifier customer.</param>
        /// <remarks>
        /// i codici sono raggruppati per fornitore...
        /// </remarks>
        /// <returns></returns>
        public ReturnValue<List<GroupedNameValue<string, Guid>>> GetCodiciFornitoriCliente(Guid idCustomer)
        {
            ReturnValue<List<GroupedNameValue<string, Guid>>> retValue = new ReturnValue<List<GroupedNameValue<string, Guid>>>(null);
            retValue.HasValue = false;

            var objs = from x in m_ctx.FCD_GetCodiciFornitoreCliente(idCustomer).ResultSet1
                       orderby x.codice
                       select new GroupedNameValue<string, Guid>()
                       {
                           name = x.codice,
                           value = x.id,
                           Group = x.fornitore
                       };

            if (objs.Any())
            {
                retValue.HasValue = true;
                retValue.Value = objs.ToList();
            }

            return retValue;
        }

        /// <summary>
        /// Ritorna l'elenco dei codici fornitori assegnabili, cioè quei codici che sono generali e 
        /// quelli relativi ad un utente non ancora assegnati...
        /// </summary>
        /// <param name="idFornitore">The identifier fornitore.</param>
        /// <param name="idCustomer">The identifier customer.</param>
        /// <remarks>
        /// I codici generali sono assegnabili ad articoli appartenenti ad utenti diversi
        /// </remarks>
        /// <returns></returns>
        public ReturnValue<List<NameValue<string, Guid>>> GetCodiciFornitoreAssegnabili(Guid idFornitore, Guid idCustomer)
        {
            ReturnValue<List<NameValue<string, Guid>>> retValue = new ReturnValue<List<NameValue<string, Guid>>>(null);
            retValue.HasValue = false;

            var objs = from x in m_ctx.FCD_GetCodiciFornitoreAssegnabili(idFornitore, idCustomer)
                       orderby x.codice
                       select new NameValue<string, Guid>()
                       {
                           name = x.codice,
                           value = x.id
                       };

            if (objs.Any())
            {
                retValue.HasValue = true;
                retValue.Value = objs.ToList();
            }

            return retValue;
        }

        /// <summary>
        /// Ritorna i codici del fornitore assegnabili ed il codice fornitore assegnato all'articolo...
        /// </summary>
        /// <param name="idArticolo">The identifier articolo.</param>
        /// <param name="idFornitore">The identifier fornitore.</param>
        /// <param name="idCustomer">The identifier customer.</param>
        /// <returns></returns>
        public ReturnValue<List<NameValue<string, Guid>>> GetCodiciFornitoreAssegnabiliEAssegnato(Guid idArticolo, Guid idFornitore, Guid idCustomer)
        {
            ReturnValue<List<NameValue<string, Guid>>> retValue = new ReturnValue<List<NameValue<string, Guid>>>(null);
            retValue.HasValue = false;

            var objs = from x in m_ctx.FCD_GetCodiceFornitoreAssegnatoECodiciFornitoreAssegnabili(idCustomer, idFornitore, idArticolo)
                       select new NameValue<string, Guid>()
                       {
                           name = x.codice,
                           value = x.id.Value
                       };

            if (objs.Any())
            {
                retValue.HasValue = true;
                retValue.Value = objs.ToList();
            }

            return retValue;
        }

        /// <summary>
        /// Cambia il codice fornitore associato ad un articolo...
        /// </summary>
        /// <param name="idArticolo">The identifier articolo.</param>
        /// <param name="vecchioCodice">The vecchio codice.</param>
        /// <param name="nuovoCodice">The nuovo codice.</param>
        /// <returns></returns>
        public ReturnValue<bool> CambiaCodice(Guid idArticolo, Guid vecchioCodice, Guid nuovoCodice)
        {
            ReturnValue<bool> retValue = new ReturnValue<bool>(false);
            retValue.Value = false;

            try
            {
                m_ctx.FCD_CancellaCodiceFornitoreArticolo(vecchioCodice, idArticolo);
                m_ctx.FCD_AssociaCodiceFornitoreArticolo(nuovoCodice, idArticolo);

                retValue.HasValue = true;
                retValue.Value = true;
            }
            catch (Exception ex)
            {
                retValue.HasError = true;
                retValue.Error = ex;
            }

            return retValue;
        }

        public ReturnValue<bool> SaveAssociazioneArticoli(Guid idArticolo, Guid idCodiceFornitore, TipoAssociazioneCodiceFornitore tipoAssociazione)
        {
            ReturnValue<bool> retValue = new ReturnValue<bool>(false);

            ReturnValue<FCD_CodiciFornitori> m_codiceFRes = this.Get(idCodiceFornitore, "FCD_AnagraficaArticolis");

            if (m_codiceFRes.HasValue && m_codiceFRes.Value != null)
            {
                FCD_CodiciFornitori m_codiceFornitore = m_codiceFRes.Value;

                if (m_codiceFornitore != null)
                {
                    m_ctx.FCD_AssociaCodiceFornitoreArticolo(m_codiceFornitore.Id, idArticolo);

                    retValue.HasValue = true;
                    retValue.Value = true;
                }
            }

            return retValue;
        }

        /// <summary>
        /// Rimuove l'associazione tra un articolo ed un codice fornitore...
        /// </summary>
        /// <param name="idArticolo">The identifier articolo.</param>
        /// <param name="idCodiceFornitore">The identifier codice fornitore.</param>
        /// <returns></returns>
        public ReturnValue<bool> RemoveAssociazioneArticolo(Guid idArticolo, Guid idCodiceFornitore)
        {
            ReturnValue<bool> retValue = new ReturnValue<bool>(false);

            try
            {
                m_ctx.FCD_CancellaCodiceFornitoreArticolo(idCodiceFornitore, idArticolo);

                retValue.HasValue = true;
                retValue.Value = true;
            }
            catch (Exception ex)
            {
                retValue.HasError = true;
                retValue.Error = ex;
                retValue.Message = ex.Message;
            }

            return retValue;
        }

        /// <summary>
        /// Rimuove l'associzione tra un articolo ed eventuali codici fornitore...
        /// </summary>
        /// <param name="idArticolo">The identifier articolo.</param>
        /// <returns></returns>
        public ReturnValue<bool> RemoveAssociazioneArticoli(Guid idArticolo)
        {
            ReturnValue<bool> retValue = new ReturnValue<bool>(false);

            try
            {
                m_ctx.FCD_CancellaCodiceFornitoreArticoloByArticolo(idArticolo);

                retValue.HasValue = true;
                retValue.Value = true;
            }
            catch (Exception ex)
            {
                retValue.HasError = true;
                retValue.Error = ex;
                retValue.Message = ex.Message;
            }

            return retValue;
        }

        public ReturnValue<bool> SaveAssociazioneArticoli_OLD(Guid idArticolo, Guid idCodiceFornitore, TipoAssociazioneCodiceFornitore tipoAssociazione)
        {
            ReturnValue<bool> retValue = new ReturnValue<bool>(false);

            ReturnValue<FCD_CodiciFornitori> m_codiceFRes = this.Get(idCodiceFornitore, "FCD_AnagraficaArticolis");

            if (m_codiceFRes.HasValue && m_codiceFRes.Value != null)
            {
                FCD_CodiciFornitori m_codiceFornitore = m_codiceFRes.Value;

                using (TransactionScope scope = new TransactionScope())
                {
                    if (tipoAssociazione == TipoAssociazioneCodiceFornitore.Update)
                    {
                        // rimuovi l'associazione prima di aggiornarla...
                        List<FCD_AnagraficaArticoli> m_associatedCustomers = m_codiceFornitore.FCD_AnagraficaArticolis.ToList();
                        foreach (FCD_AnagraficaArticoli m_c in m_associatedCustomers)
                        {
                            m_codiceFornitore.FCD_AnagraficaArticolis.Remove(m_c);
                        }
                    }

                    // aggiorna l'associazione...
                    try
                    {
                        using (AnagraficaArticoliFacade m_cf = AnagraficaArticoliFacade.Create())
                        {
                            ReturnValue<FCD_AnagraficaArticoli> m_resA = m_cf.Get(idArticolo);
                            if (m_resA.HasValue && m_resA.Value != null)
                            {
                                // insert into many-to-many...
                                m_ctx.FCD_AnagraficaArticolis.Attach(m_resA.Value);
                                m_codiceFornitore.FCD_AnagraficaArticolis.Add(m_resA.Value);
                            }
                        }
                    }
                    catch
                    {
                        // protezione per qualsiasi evenienza, in modo che finisca sempre bene...
                    }

                    m_ctx.SaveChanges();

                    scope.Complete();
                }
            }

            return retValue;
        }

        public ReturnValue<bool> RemoveAssociazioneArticoli_OLD(Guid idArticolo)
        {
            ReturnValue<bool> retValue = new ReturnValue<bool>(false);

            try
            {
                ReturnValue<FCD_AnagraficaArticoli> m_articoliRes = new ReturnValue<FCD_AnagraficaArticoli>(null);
                using (AnagraficaArticoliFacade m_aaf = AnagraficaArticoliFacade.Create())
                {
                    m_articoliRes = m_aaf.Get(idArticolo, "FCD_CodiciFornitoris");

                    if (m_articoliRes.HasValue && m_articoliRes.Value != null)
                    {
                        FCD_AnagraficaArticoli m_articolo = m_articoliRes.Value;

                        if (m_articolo != null)
                        {
                            List<FCD_CodiciFornitori> m_associatedCodes = m_articolo.FCD_CodiciFornitoris.ToList();

                            using (TransactionScope scope = new TransactionScope())
                            {
                                foreach (FCD_CodiciFornitori m_c in m_associatedCodes)
                                {
                                    m_articolo.FCD_CodiciFornitoris.Remove(m_c);
                                }

                                m_ctx.SaveChanges();

                                scope.Complete();
                            }
                        }
                    }
                }

                retValue.HasValue = true;
                retValue.Value = true;
            }
            catch (Exception ex)
            {
                retValue.HasError = true;
                retValue.Error = ex;
                retValue.Message = ex.Message;
            }

            return retValue;
        }
    }
}
